package org.budo.graph.service.util;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.budo.ehcache.config.annotation.entity.EhCacheConfigEntity;
import org.budo.ehcache.config.annotation.factory.BudoEhcacheAnnotationConfigFactoryBean;
import org.budo.graph.Constant;
import org.budo.graph.entity.EhcacheStatistics;
import org.budo.graph.entity.GuavaCacheStatistics;
import org.budo.support.freemarker.FreemarkerUtil;
import org.budo.support.google.common.cache.GoogleCacheUtil;
import org.budo.support.lang.util.MapUtil;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.servlet.util.QueryStringUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.support.spring.context.aware.BudoApplicationContextAware;
import org.budo.support.spring.io.util.ResourceUtil;
import org.budo.support.spring.util.SpringUtil;
import org.slf4j.Logger;
import org.springframework.context.ApplicationContext;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;

/**
 * @author lmw
 */
public class EhcacheStatisticsUtil {
    private static final Logger log = Slf4j.getLogger();

    private static final boolean HAS_EHCACHE = ReflectUtil.hasClass("net.sf.ehcache.Cache");

    private static Map<String, EhCacheConfigEntity> _cacheConfigMap;

    public static List<EhcacheStatistics> listEhcacheStatistics(CacheManager cacheManager) {
        List<EhcacheStatistics> list = new ArrayList<EhcacheStatistics>();

        String[] cacheNames = cacheManager.getCacheNames();
        if (null != cacheNames && cacheNames.length > 0) {
            for (String cacheName : cacheNames) {
                Cache cache = cacheManager.getCache(cacheName);
                EhcacheStatistics ehcacheStatistics = new EhcacheStatistics(cache);
                list.add(ehcacheStatistics);
            }
        }

        Map<String, com.google.common.cache.Cache> guavaCache = GoogleCacheUtil.guavaCache();
        if (null != guavaCache && !guavaCache.isEmpty()) {
            for (Entry<String, com.google.common.cache.Cache> entry : guavaCache.entrySet()) {
                GuavaCacheStatistics guavaCacheStatistics = new GuavaCacheStatistics(entry.getKey(), entry.getValue());
                list.add(guavaCacheStatistics);
            }
        }

        return list;
    }

    public static EhCacheConfigEntity getEhCacheConfigEntity(String cacheName) {
        Map<String, EhCacheConfigEntity> conf = getEhCacheConfigEntityMap();
        return null == conf ? null : conf.get(cacheName);
    }

    public static Map<String, EhCacheConfigEntity> getEhCacheConfigEntityMap() {
        if (null != _cacheConfigMap) {
            return _cacheConfigMap;
        }

        BudoApplicationContextAware applicationContextAware = BudoApplicationContextAware.getInstance();
        if (null == applicationContextAware) {
            log.warn("#51 BudoApplicationContextAware is null");
            return null;
        }

        ApplicationContext applicationContext = applicationContextAware.getApplicationContext();
        if (null == applicationContext) {
            log.warn("#57 applicationContext is null, applicationContextAware=" + applicationContextAware);
            return null;
        }

        BudoEhcacheAnnotationConfigFactoryBean configBean = SpringUtil.getBean(applicationContext, BudoEhcacheAnnotationConfigFactoryBean.class);
        if (null == configBean) {
            log.warn("#63 BudoEhcacheAnnotationConfigFactoryBean not found, applicationContext=" + applicationContext);
            return null;
        }

        _cacheConfigMap = configBean.getCacheConfigMap();
        if (null == _cacheConfigMap) {
            log.warn("#69 getEhCacheConfigEntity not found, configBean=" + configBean);
            return null;
        }

        return _cacheConfigMap;
    }

    public static String writeEhcacheStatisticsResponse(ApplicationContext applicationContext, String url) {
        try {
            if (!HAS_EHCACHE) {
                return "NO_EHCACHE";
            }

            CacheManager cacheManager = SpringUtil.getBean(applicationContext, CacheManager.class);
            if (null == cacheManager) {
                return "eh.CacheManager in applicationContext is null";
            }

            if (StringUtil.startsWith(url, "/ehcache-statistics-clear-")) {
                String name = url.replace("/ehcache-statistics-clear-", "");
                return cacheClear(name);
            } else {
                String inputString = ResourceUtil.classPathResourceInputStreamToString(Constant.GRAPH_TEMPLATE_PATH + "/ehcache-statistics.htm");
                List<EhcacheStatistics> cacheStatList = listEhcacheStatistics(cacheManager);

                Map<String, Object> data = MapUtil.stringObjectMap("cacheStatList", cacheStatList, //
                        "RequestParameters", QueryStringUtil.queryStringToStringMap(url));

                return FreemarkerUtil.freemarkerRender(inputString, data);
            }
        } catch (Throwable e) {
            log.error("#114 writeEhcacheStatisticsResponse error, e=" + e, e);
            return "ehcache_statistics_error_" + e;
        }
    }

    private static String cacheClear(String name) {
        String resp = "";

        Cache ehCache = getEhCache(name);
        if (null != ehCache) {
            log.warn("#131 ehcache[" + name + "] cleared");

            ehCache.removeAll();

            resp += "ehcache[" + name + "] cleared; ";
        } else {
            log.warn("#139 ehcache[" + name + "] not found");
            resp += "ehcache[" + name + "] not found; ";
        }

        com.google.common.cache.Cache guavaCache = GoogleCacheUtil.getCache(name);
        if (null != guavaCache) {
            log.warn("#145 guavaCache[" + name + "] cleared");

            guavaCache.cleanUp();
            guavaCache.invalidateAll();

            resp += "guavaCache[" + name + "] cleared; ";
        } else {
            log.warn("#151 guavaCache[" + name + "] not found");
            resp += "guavaCache[" + name + "] not found; ";
        }

        return resp;
    }

    private static Cache getEhCache(String name) {
        CacheManager cacheManager = SpringUtil.getBean(CacheManager.class);
        if (null == cacheManager) {
            return null;
        }

        Cache cache = cacheManager.getCache(name);
        return cache;
    }
}
